package cn.org.tpeach.nosql.controller;

import cn.org.tpeach.nosql.bean.DicBean;
import cn.org.tpeach.nosql.bean.PageBean;
import cn.org.tpeach.nosql.bean.RedisKeyDataVo;
import cn.org.tpeach.nosql.enums.RedisTreeType;
import cn.org.tpeach.nosql.framework.LarkFrame;
import cn.org.tpeach.nosql.framework.ServiceProxy;
import cn.org.tpeach.nosql.redis.bean.RedisKeyData;
import cn.org.tpeach.nosql.redis.bean.RedisKeyInfo;
import cn.org.tpeach.nosql.redis.bean.RedisTreeItem;
import cn.org.tpeach.nosql.redis.controller.BaseController;
import cn.org.tpeach.nosql.redis.controller.ResultRes;
import cn.org.tpeach.nosql.redis.service.IRedisConnectService;
import cn.org.tpeach.nosql.service.ICommonService;
import cn.org.tpeach.nosql.tools.*;
import cn.org.tpeach.nosql.view.CenteredOverrunTableCell;
import cn.org.tpeach.nosql.view.JavaFxUtil;
import io.lettuce.core.ScanCursor;
import javafx.beans.InvalidationListener;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.util.Callback;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.awt.*;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.regex.Pattern.compile;

@Slf4j
public class KeyInfoController implements Initializable , Closeable {
    private final IRedisConnectService redisConnectService = ServiceProxy.getBeanProxy("redisConnectService", IRedisConnectService.class);
    private final ICommonService commonService = ServiceProxy.getBeanProxy("commonService", ICommonService.class);
    private boolean isFirst = true;
    @FXML
    private BorderPane keyInfoBorderPane;
    @FXML
    private Button keyInfoDeleteButton;
    @FXML
    private Button keyInfoRefreshButton;
    @FXML
    private TextField keyTextField;
    @FXML
    private HBox keyInfoTypeHbox;
    @FXML
    private HBox keyInfoPageHbox;
    @FXML
    private Label keyInfoTypeLabel;
    @FXML
    private Button keyInfoLeftAddRowButton;
    @FXML
    private Button keyInfoRightAddRowButton;
    @FXML
    private Button keyInfoImportButton;
    @FXML
    private Button keyInfoBatchDeleteButton;
    @FXML
    private Button keyInfoSaveButton;
    @FXML
    private TextField keyInfoFilterTextField;
    @FXML
    private TextField redisKeyInfoTTLField;
    @FXML
    private TextField redisKeyInfoSizeField;
    @FXML
    private TextField redisKeyInfoIdleTimeField;
    @FXML
    private TextArea keyInfoValueTextArea;
    @FXML
    private TableView<RedisKeyData> redisKeyInfoTableView;

    @FXML
    private ComboBox<Integer> pageRowComboBox;
    @FXML
    private ComboBox<DicBean> keyInfoShowTypeCombox;
    @FXML
    private ComboBox<DicBean> keyInfoTableShowTypeCombox;
    @FXML
    private Label resultFirstLabel;
    @FXML
    private Label resultPreviousLabel;
    @FXML
    private Label resultNextLabel;
    @FXML
    private Label resultLastLabel;
    @FXML
    private Label keyInfoTotalPageLabel;
    @FXML
    private Label keyInfoValueSizeLabel;
    @FXML
    private Text keyInfoValueSizeText;
    @FXML
    private TextField keyInfoGotoPageTextField;


    @Setter
    private TreeItem<RedisTreeItem>  selectedItem;
    @Setter
    private Tab tab;
    private PageBean pageBean = new PageBean();
    private RedisKeyInfo keyInfo;
    private  TableColumn<RedisKeyData,Number> tbIndexCol = new TableColumn<>("#");
    private  TableColumn<RedisKeyData,String> tbScoreCol = new TableColumn<>("Score");
    private  TableColumn<RedisKeyData,String> tbFieldCol = new TableColumn<>("Field");
    private  TableColumn<RedisKeyData,String> tbValueCol = new TableColumn<>("Value");
    private  TableColumn<RedisKeyData,RedisKeyData> tbOperateCol = new TableColumn<>("Operate");

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        keyInfoDeleteButton.setGraphic(new ImageView("/view/img/whiteDelete.png"));
        keyInfoRefreshButton.setGraphic(new ImageView("/view/img/whiteRefresh.png"));

        JavaFxUtil.setTooltipText(keyInfoImportButton,keyInfoImportButton.getText());
        JavaFxUtil.setTooltipText(keyInfoBatchDeleteButton,keyInfoBatchDeleteButton.getText());
        JavaFxUtil.setTooltipText(keyInfoSaveButton,keyInfoSaveButton.getText());


        keyInfoTypeHbox.widthProperty().addListener((observable, oldValue, newValue) -> {
            if(keyInfoTypeLabel.getWidth() > 0){
                keyInfoFilterTextField.setPrefWidth(newValue.doubleValue() - keyInfoTypeLabel.getWidth() - keyInfoTypeHbox.getSpacing() * 2 -keyInfoTypeHbox.getInsets().getRight());
            }
        });
        keyInfoTypeHbox.heightProperty().addListener((observable, oldValue, newValue) -> keyInfoTypeLabel.setPrefHeight(newValue.doubleValue()));
        keyInfoTypeLabel.widthProperty().addListener((observable, oldValue, newValue) -> keyInfoFilterTextField.setMinWidth(keyInfoTypeHbox.getWidth() - newValue.doubleValue()));
        keyInfoFilterTextField.heightProperty().addListener((observableValue, oldValue, newValue) -> {
            double height = newValue.doubleValue();
            keyInfoTypeLabel.setPrefHeight(height);
        });
        tbIndexCol.setCellValueFactory(new PropertyValueFactory<>("index"));
        tbIndexCol.setUserData("index");
        tbScoreCol.setCellValueFactory(param -> {
            SimpleStringProperty simpleStringProperty = new SimpleStringProperty();
            simpleStringProperty.set(StringUtils.decimalFormat.format(param.getValue().getScore()));
            return simpleStringProperty;
        });
        tbScoreCol.setUserData("score");
        tbFieldCol.setCellValueFactory(param -> {
            SimpleStringProperty simpleStringProperty = new SimpleStringProperty();
            simpleStringProperty.set(StringUtils.showHexStringValue(ConfigParserUtils.getInstance().byteToStr(param.getValue().getField())));
            return simpleStringProperty;
        });
        tbFieldCol.setUserData("field");
        tbValueCol.setCellValueFactory(param -> {
            SimpleStringProperty simpleStringProperty = new SimpleStringProperty();
            simpleStringProperty.set(StringUtils.showHexStringValue(ConfigParserUtils.getInstance().byteToStr(param.getValue().getValue())));
            return simpleStringProperty;
        });
        tbValueCol.setUserData("value");
        tbValueCol.setCellFactory(param -> new CenteredOverrunTableCell<>("..."));
        tbScoreCol.setCellFactory(param -> new CenteredOverrunTableCell<>("...",true));
        tbFieldCol.setCellFactory(param -> new CenteredOverrunTableCell<>("..."));

        operateColumnInit();
        tbIndexCol.setMaxWidth(500);
        tbOperateCol.setMaxWidth(1800);
        redisKeyInfoTableView.getColumns().addAll(tbIndexCol,tbScoreCol,tbFieldCol,tbValueCol,tbOperateCol);
//        redisKeyInfoTableView.setTableMenuButtonVisible(true);
        redisKeyInfoTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        redisKeyInfoTableView.getSelectionModel().setCellSelectionEnabled(false);
        redisKeyInfoTableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->
                keyInfoBatchDeleteButton.setDisable(DataStructureUtils.isEmpty(redisKeyInfoTableView.getSelectionModel().getSelectedItems())));
        DicBean plainText = new DicBean("1", "PlainText");
        DicBean table = new DicBean("1", "table");
        keyInfoShowTypeCombox.getItems().addAll(
                plainText,
                new DicBean("2", "Json"),
                new DicBean("3", "Hex Plain"),
                new DicBean("4", "Hex")
        );
        keyInfoShowTypeCombox.setValue(plainText);
        keyInfoTableShowTypeCombox.getItems().addAll(
                table,
//                new DicBean("2", "文本"), 文本太大时会卡顿 移除该功能
                new DicBean("3", "Json")
        );
        keyInfoTableShowTypeCombox.setValue(table);
        pageRowComboBox.getItems().addAll(100,200,500,1000,10000);
        pageRowComboBox.setValue(100);
        resultFirstLabel.setText("");
        resultPreviousLabel.setText("");
        resultNextLabel.setText("");
        resultLastLabel.setText("");

        resultFirstLabel.setGraphic(new ImageView("/view/img/page/result_first.png"));
        resultPreviousLabel.setGraphic(new ImageView("/view/img/page/result_previous.png"));
        resultNextLabel.setGraphic(new ImageView("/view/img/page/result_next.png"));
        resultLastLabel.setGraphic(new ImageView("/view/img/page/result_last.png"));
        JavaFxUtil.setTooltipText(pageRowComboBox,"每页显示数");
        JavaFxUtil.setTooltipText(resultFirstLabel,"移动至首页");
        JavaFxUtil.setTooltipText(resultPreviousLabel,"移动到上一页");
        JavaFxUtil.setTooltipText(resultNextLabel,"移动到下一页");
        JavaFxUtil.setTooltipText(resultLastLabel,"移动到尾页");
        JavaFxUtil.setTooltipText(keyInfoGotoPageTextField,"跳转页");
        //-- --
        keyInfoDeleteButton.setOnAction(event ->  commonService.deleteKey(selectedItem, selectedItem.getValue()));

        keyInfoRefreshButton.setOnAction(event -> KeyInfoController.this.refresh());

        keyInfoValueTextArea.textProperty().addListener((observable, oldValue, newValue) -> {
            if("1".equals(keyInfoShowTypeCombox.getValue().getCode())){
                keyInfoValueTextArea.getProperties().put("text",newValue);
                if(keyInfoValueTextArea.getUserData() != null){
                    keyInfoSaveButton.setDisable(StringUtils.equals(newValue,keyInfoValueTextArea.getUserData().toString()));
                }
            }else{
                keyInfoSaveButton.setDisable(true);
            }

        });
        pageRowComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->  KeyInfoController.this.refresh(false,false));

        keyInfoShowTypeCombox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> commonService.getSelectDicText(keyInfoValueTextArea,newValue,keyInfoSaveButton));
        keyInfoTableShowTypeCombox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> changeTableShowType(newValue));
    }

    private void changeTableShowType(DicBean newValue) {
        keyInfoValueTextArea.setEditable(false);
        if("1".equals(newValue.getCode())){
            keyInfoBorderPane.setCenter(redisKeyInfoTableView);
            keyInfoValueTextArea.setText("");
        }else if("2".equals(newValue.getCode())){
            //获取显示的表格  文本太大时会卡顿
            ObservableList<TableColumn<RedisKeyData, ?>> columns = redisKeyInfoTableView.getColumns();
            List<String> list = new ArrayList<>();
            List<String> fields = new ArrayList<>();
            for (TableColumn<RedisKeyData, ?> column : columns) {
                if(column.isVisible() && column.getUserData() != null){
                    list.add(column.getText());
                    fields.add(column.getUserData().toString());
                }

            }
            String[] title = new String[list.size()];
            list.toArray(title);
            TextForm.TextFormBulider bulider = TextForm.bulider().title(title);
            redisKeyInfoTableView.getItems().forEach(data -> {
                String[] row = new String[fields.size()];
                RedisKeyDataVo keyDataVo = RedisKeyDataVo.build(data);
                for (int i = 0; i < fields.size(); i++) {
                    try {
                        Object property = ReflectUtil.getProperty(keyDataVo, fields.get(i));
                        if(property != null){
                             row[i] = property.toString();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                bulider.addRow(row);
            });

//            keyInfoValueTextArea.setText();
            keyInfoBorderPane.setCenter(new Label(bulider.finish().formatTable()));
        }else if("3".equals(newValue.getCode())){
            if(DataStructureUtils.isNotEmpty(keyInfo.getRedisKeyDataList())){
                List<RedisKeyDataVo> keyDataVos = keyInfo.getRedisKeyDataList().stream().map(data -> RedisKeyDataVo.build(data)).collect(Collectors.toList());
                keyInfoValueTextArea.setText(GsonUtil.gson2String(keyDataVos));
            }else{
                keyInfoValueTextArea.setText("[]");
            }
            keyInfoBorderPane.setCenter(keyInfoValueTextArea);
        }
    }


    @FXML
    public void renameKey(MouseEvent event){
        if (event.getClickCount() == 2) {
            commonService.renameKey(selectedItem,selectedItem.getValue());
        }
    }



    @FXML
    public void filterTable(ActionEvent event){
        String text = keyInfoFilterTextField.getText();
        if(keyInfo != null && RedisTreeType.LIST.equals(keyInfo.getType())){
            if(StringUtils.isBlank(text)){
                redisKeyInfoTableView.setItems(FXCollections.observableArrayList(keyInfo.getRedisKeyDataList()));
                return;
            }
            Pattern pattern =compile(".*"+ text.trim().replaceAll("\\*",".*")+".*");
            List<RedisKeyData> redisKeyDataList = keyInfo.getRedisKeyDataList();
            if(DataStructureUtils.isNotEmpty(redisKeyDataList)){
                ObservableList<RedisKeyData> list = FXCollections.observableArrayList();
                for (RedisKeyData redisKeyData : redisKeyDataList) {
                    byte[] value = redisKeyData.getValue();
                    String v = StringUtils.showHexStringValue(ConfigParserUtils.getInstance().byteToStr(value));
                    if(pattern.matcher(v).find()){
                        list.add(redisKeyData);
                    }
                }
                redisKeyInfoTableView.setItems(list);
                redisKeyInfoTableView.refresh();
            }
        }else{
            refresh(false,false);
        }
    }

    @FXML
    public void expireKey(MouseEvent event) {
        if (event.getClickCount() != 2) {
            return;
        }
        TextInputDialog dialog = new TextInputDialog(keyInfo.getTtl()+"");
        dialog.setContentText("NEW TTL:");
        dialog.setTitle("Set key TTL");
        dialog.setHeaderText(null);
        Optional<String> result = dialog.showAndWait();
        result.ifPresent(text ->{
            if(StringUtils.isNotBlank(text)){
                try {
                    Integer ttl = Integer.valueOf(text);
                    Integer newTTL = ttl < 0 ? -1 : ttl;
                    if (keyInfo.getTtl() != null && newTTL.equals(keyInfo.getTtl().intValue())) {
                        return;
                    }
                    ResultRes<Boolean> resultRes = BaseController.dispatcher(() -> redisConnectService.expireKey(keyInfo.getId(), keyInfo.getDb(), keyInfo.getKey(), newTTL));
                    if (resultRes.isRet() && resultRes.getData()) {
                        keyInfo.setTtl(Long.valueOf(newTTL));
                        redisKeyInfoTTLField.setText(newTTL+"");
                    }else {
                        JavaFxUtil.showMessageErrorDialog(null,null,"设置TTL失败:" + resultRes.getMsg());
                    }
                }catch (NumberFormatException e){
                    JavaFxUtil.showMessageErrorDialog(null,null,"请输入数字");
                }
            }else{
                JavaFxUtil.showMessageErrorDialog(null,null,"请输入数字");
            }
        });
    }
    @FXML
    public void addRowToKeyLeft(ActionEvent event){
        addRowToKey(event,null,true,false);
    }
    @FXML
    public void addRowToKeyRight(ActionEvent event){
        addRowToKey(event,null,false,false);
    }

    @FXML
    public void resultFirstClick(MouseEvent event){
        pageBean.setPage(1);
        refresh(false,false);
    }
    @FXML
    public void resultPreviousClick(MouseEvent event){
        pageBean.setPage(pageBean.getPage()-1);
        refresh(false,false);
    }

    @FXML
    public void resultNextClick(MouseEvent event){
        pageBean.setPage(pageBean.getPage()+1);
        refresh(false,false);
    }

    @FXML
    public void resultLastClick(MouseEvent event){
        pageBean.setPage(pageBean.getTotalPage());
        refresh(false,false);
    }

    @FXML
    public void gotoPage(ActionEvent event){
        String text = keyInfoGotoPageTextField.getText();
        try {
            Integer page = Integer.valueOf(text);
            if(page > 0 && page <= pageBean.getTotalPage()){
                pageBean.setPage(page);
                refresh(false,false);
            }else{
                JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,"请输入正确的页数");
            }
        }catch (NumberFormatException e){
            JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,"请输入正确的页数");
        }
    }



    private void addRowToKey(Event event, RedisKeyData data, Boolean isLeft,boolean isShow){
        try {
            FXMLLoader fxmlLoader = new FXMLLoader();
            AtomicBoolean canClose = new AtomicBoolean(false);
            Node node = JavaFxUtil.loadFxml(fxmlLoader,"/view/fxml/tableRowInfoToKey.fxml");
            Dialog<Object> dialog = JavaFxUtil.createDialog(node);
            ButtonType configButtonType = new ButtonType("确定", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(configButtonType, ButtonType.CANCEL);
            TableRowInfoToKeyController controller = fxmlLoader.getController();
            Button configButton = (Button) dialog.getDialogPane().lookupButton(configButtonType);
            Button cancelButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.CANCEL);
            controller.setConfigButton(configButton);
            controller.init(keyInfo,data,isLeft,isShow);

            cancelButton.setOnAction(event1 -> {
                canClose.set(true);
                dialog.close();
            });
            dialog.setOnCloseRequest(event1 -> {
                if(!canClose.get()){
                    event1.consume();
                }
            });
            configButton.setOnAction(event1 -> {
                boolean b = JavaFxUtil.showConfirmDialogYNC("保存确认", "是否确认保存？");
                if(b) {
                    b = controller.submit();
                    if(b){
                        canClose.set(true);
                        dialog.close();
                        this.refresh(true,false);
                    }
                }
            });
            dialog.show();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存string类型
     * @param event
     */
    @FXML
    public void saveKeyInfo(ActionEvent event){
        if(!RedisTreeType.STRING.equals(keyInfo.getType())){
            return;
        }
        boolean b = JavaFxUtil.showConfirmDialogYNC("保存确认", "是否确认保存？");
        if(b){
            RedisKeyInfo newKeyInfo  = new RedisKeyInfo();
            ReflectUtil.copyProperties(keyInfo, newKeyInfo);
            newKeyInfo.setValue(StringUtils.isEmpty(keyInfoValueTextArea.getText()) ? null : ConfigParserUtils.getInstance().strToByte(keyInfoValueTextArea.getText()));
            ResultRes<RedisKeyInfo> res = BaseController.dispatcher(() -> redisConnectService.updateKeyInfo(newKeyInfo, keyInfo));
            if(res.isRet()){
                JavaFxUtil.showMessageInfoDialog(null,null,"保存成功");
                this.refresh(true,false);
            }else{
                JavaFxUtil.showMessageErrorDialog(null,null,res.getMsg());
            }
        }
    }

    @FXML
    private void batchDelete(ActionEvent event){
        ObservableList<RedisKeyData> selectedItems = redisKeyInfoTableView.getSelectionModel().getSelectedItems();
        boolean b = JavaFxUtil.showConfirmDialogYNC("批量删除", "选择"+selectedItems.size()+ "条记录，是否确认删除？");
        if(!b){
            return;
        }
        List<RedisKeyData> dataList = new ArrayList<>(selectedItems);
        ResultRes<Integer> resultRes = BaseController.dispatcher(() -> redisConnectService.deleteRowKeyInfo(
                keyInfo.getId(), keyInfo.getDb(), keyInfo.getKey(), keyInfo.getType(),dataList));
        if (resultRes.isRet()) {
            JavaFxUtil.showMessageInfoDialog(null,null,"删除成功,数量："+resultRes.getData());
            KeyInfoController.this.refresh();
        }else{
            JavaFxUtil.showMessageErrorDialog(null,null,"删除失败："+resultRes.getMsg());
        }
    }

    @FXML
    private void keyInfoImport(ActionEvent event){
        FileChooser fileChooser = new FileChooser();
        FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("CSV files (*.csv)", "*.csv");
        fileChooser.getExtensionFilters().add(extFilter);
        File file = fileChooser.showSaveDialog(LarkFrame.primaryStage);
        if (file == null){
            return;
        }
        if(file.exists()){//文件已存在，则删除覆盖文件
            file.delete();
        }
        String exportFilePath = file.getAbsolutePath();
        StringBuffer sb = new StringBuffer();
        if(RedisTreeType.STRING.equals(keyInfo.getType())){
            sb.append(StringUtils.getCsvTypeStr(keyInfoValueTextArea.getText()));
        }else{
            ObservableList<TableColumn<RedisKeyData, ?>> columns = redisKeyInfoTableView.getColumns();
            List<String> fields = new ArrayList<>();
            boolean flag = false;
            //标题
            for (TableColumn<RedisKeyData, ?> column : columns) {
                if(column.isVisible() && column.getUserData() != null){
                    if(!flag){
                        flag = true;
                    }else{
                        sb.append(",");
                    }
                    sb.append(StringUtils.getCsvTypeStr(column.getText()));
                    fields.add(column.getUserData().toString());
                }

            }
            sb.append("\r\n");
            ObservableList<RedisKeyData> items = redisKeyInfoTableView.getItems();
            for (RedisKeyData data : items) {
                flag = false;
                RedisKeyDataVo keyDataVo = RedisKeyDataVo.build(data);
                for (int i = 0; i < fields.size(); i++) {
                    if (!flag) {
                        flag = true;
                    } else {
                        sb.append(",");
                    }
                    try {
                        Object property = ReflectUtil.getProperty(keyDataVo, fields.get(i));
                        sb.append(StringUtils.getCsvTypeStr(property.toString()));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                sb.append("\r\n");
            }
            try {
                IOUtil.writeConfigFile(sb.toString(),file);
                JavaFxUtil.showMessageInfoDialog(null,null,"导出成功,格式为UTF-8");
                Desktop.getDesktop().open(file.getParentFile());
            } catch (IOException e) {
                JavaFxUtil.showMessageInfoDialog(null,null,"导出失败："+e.getMessage());
                log.error("导出失败",e);
            }

        }
    }

    private void operateColumnInit() {
        tbOperateCol.setCellValueFactory(param -> new ObservableValue<RedisKeyData>() {
            @Override
            public void addListener(ChangeListener<? super RedisKeyData> listener) { }
            @Override
            public void removeListener(ChangeListener<? super RedisKeyData> listener) { }
            @Override
            public RedisKeyData getValue() {
                return param.getValue();
            }
            @Override
            public void addListener(InvalidationListener listener) {
            }
            @Override
            public void removeListener(InvalidationListener listener) {
            }
        });
        tbOperateCol.setCellFactory(new Callback<TableColumn<RedisKeyData, RedisKeyData>, TableCell<RedisKeyData, RedisKeyData>>() {
            @Override
            public TableCell<RedisKeyData, RedisKeyData> call(TableColumn<RedisKeyData, RedisKeyData> param) {
                return new TableCell<RedisKeyData, RedisKeyData>(){
                    @Override
                    protected void updateItem(RedisKeyData item, boolean empty) {
                        super.updateItem(item, empty);
                        if(!empty && item != null){
                            HBox hBox = new HBox();
                            hBox.setAlignment(Pos.CENTER);
                            hBox.setSpacing(10);
                            Label editLabel = new Label("",new ImageView("/view/img/edit.png"));
                            Label deleteLabel = new Label("",new ImageView("/view/img/blueDelete.png"));
                            Label showLabel = new Label("",new ImageView("/view/img/eye.png"));
                            editLabel.setOnMouseClicked(event -> addRowToKey(event,item,null,false));
                            showLabel.setOnMouseClicked(event -> addRowToKey(event,item,null,true));
                            deleteLabel.setOnMouseClicked(event -> {
                                if(RedisTreeType.STRING.equals(KeyInfoController.this.keyInfo.getType())){
                                    return;
                                }
                                boolean b = JavaFxUtil.showConfirmDialogYNC("删除确认", "是否确认删除?");
                                if(b){
                                    RedisKeyInfo redisKeyInfo = KeyInfoController.this.keyInfo;
                                    List<RedisKeyData> dataList = new ArrayList<>(1);
                                    dataList.add(item);
                                    ResultRes<Integer> resultRes = BaseController.dispatcher(() -> redisConnectService.deleteRowKeyInfo(
                                            redisKeyInfo.getId(), redisKeyInfo.getDb(), redisKeyInfo.getKey(), redisKeyInfo.getType(),dataList));
                                    if (resultRes.isRet()) {
                                        redisKeyInfo.setSize(redisKeyInfo.getSize()-1);
                                        KeyInfoController.this.refresh(false,false);
                                    }else{
                                        JavaFxUtil.showMessageErrorDialog(null,null,"删除失败："+resultRes.getMsg());
                                    }
                                }
                            });
                            hBox.getChildren().addAll(editLabel,showLabel,deleteLabel);
                            this.setGraphic(hBox);
                        }
                    }
                };
            }
        });
    }

    public void refresh(){
        refresh(true,false);
    }
    public void refresh(boolean otherInfo,boolean isNew){
        try {
            keyInfoSaveButton.setDisable(true);
            keyInfoBatchDeleteButton.setDisable(true);
            RedisTreeItem value = selectedItem.getValue();

            String pattern = keyInfoFilterTextField.getText();
            ScanCursor cursor = keyInfo == null ? null : keyInfo.getCursor();
            RedisKeyInfo srcKeyInfo = otherInfo ? null : keyInfo;
            pageBean.setRows(pageRowComboBox.getValue());
            if (isNew) {
                pageBean.setPage(1);
            }
            ResultRes<RedisKeyInfo> res = BaseController.dispatcher(() -> redisConnectService.getKeyInfo(value.getId(), value.getDbAmount().getDbIndex(), value.getKey(), pattern, pageBean, cursor, srcKeyInfo));
            if (res.isRet()) {
                keyInfo = res.getData();
                pageBean = keyInfo.getPageBean();
                keyTextField.setText(value.getName());
                JavaFxUtil.setTooltipText(keyTextField, value.getName());
                updateKeyInfoUi();
            } else {
                JavaFxUtil.showMessageErrorDialog(null, null, res.getMsg());
                if (isFirst) {
                    ObservableList<Tab> tabs = RedisApplication.mainController.getMainInfoTabPane().getTabs();
                    if (DataStructureUtils.isNotEmpty(tabs)) {
                        tabs.remove(tab);
                        JavaFxUtil.closeTabRelease(RedisApplication.mainController.getMainInfoTabPane(), tab);
                    }
                }
            }
        }finally {
            if(isFirst){
                isFirst = false;
            }
        }

    }

    private void updateKeyInfoUi() {

        redisKeyInfoTTLField.setText(keyInfo.getTtl().toString());
        redisKeyInfoSizeField.setText(keyInfo.getSize()+"");
        redisKeyInfoIdleTimeField.setText(keyInfo.getIdleTime()+"");
        keyInfoTypeLabel.setText(keyInfo.getType().getType().toUpperCase());
        if(keyInfo.getTtl() > 0){
            JavaFxUtil.setTooltipText(redisKeyInfoTTLField, DateTools.getDatePoor(keyInfo.getTtl()*1000));
        }else{
            JavaFxUtil.setTooltipText(redisKeyInfoTTLField,keyInfo.getTtl().toString());
        }

        switch (keyInfo.getType()){
            case STRING:
                keyInfoLeftAddRowButton.setVisible(false);
                keyInfoRightAddRowButton.setVisible(false);
                keyInfoValueTextArea.setText(StringUtils.showHexStringValue(ConfigParserUtils.getInstance().byteToStr(keyInfo.getValue())));
                keyInfoValueTextArea.setUserData(keyInfoValueTextArea.getText());
                keyInfoValueTextArea.getProperties().put("text",keyInfoValueTextArea.getText());
                keyInfoValueTextArea.getProperties().put("textByte",keyInfo.getValue());
                commonService.getSelectDicText(keyInfoValueTextArea,keyInfoShowTypeCombox.getValue(),keyInfoSaveButton);
                break;
            case LIST:
                keyInfoLeftAddRowButton.setText("左添加一行");
                keyInfoRightAddRowButton.setText("右添加一行");
                JavaFxUtil.setTooltipText(keyInfoLeftAddRowButton,keyInfoLeftAddRowButton.getText());
                JavaFxUtil.setTooltipText(keyInfoRightAddRowButton,keyInfoRightAddRowButton.getText());
                keyInfoLeftAddRowButton.setVisible(true);
                keyInfoRightAddRowButton.setVisible(true);
                break;
            case SET:
            case ZSET:
            case HASH:
                keyInfoRightAddRowButton.setText("添加新行");
                JavaFxUtil.setTooltipText(keyInfoRightAddRowButton,keyInfoRightAddRowButton.getText());
                keyInfoLeftAddRowButton.setVisible(false);
                keyInfoRightAddRowButton.setVisible(true);
                break;
            default:
                break;
        }
        switch (keyInfo.getType()){
            case STRING:
                keyInfoTypeLabel.setStyle("-fx-background-color: rgb(76,174,81)");
                break;
            case LIST:
                keyInfoTypeLabel.setStyle("-fx-background-color: rgb(2, 122, 180)");
                tbFieldCol.setVisible(false);
                tbScoreCol.setVisible(false);
                tbValueCol.setText("Value");
                break;
            case SET:
                keyInfoTypeLabel.setStyle("-fx-background-color: rgb(245, 185, 15)");
                tbFieldCol.setVisible(false);
                tbScoreCol.setVisible(false);
                tbValueCol.setText("Member");
                break;
            case ZSET:
                keyInfoTypeLabel.setStyle("-fx-background-color: rgb(24, 170, 110)");
                tbFieldCol.setVisible(false);
                tbScoreCol.setVisible(true);
                tbValueCol.setText("Member");
                break;
            case HASH:
                keyInfoTypeLabel.setStyle("-fx-background-color: rgb(116, 39, 135)");
                tbFieldCol.setVisible(true);
                tbScoreCol.setVisible(false);
                tbValueCol.setText("Value");
                break;
            default:
                break;
        }
        boolean isString = RedisTreeType.STRING.equals(keyInfo.getType());
        keyInfoSaveButton.setVisible(isString);
        keyInfoSaveButton.setManaged(isString);
        keyInfoTableShowTypeCombox.setVisible(!isString);
        keyInfoTableShowTypeCombox.setManaged(!isString);
        keyInfoShowTypeCombox.setVisible(isString);
        keyInfoShowTypeCombox.setManaged(isString);

        keyInfoFilterTextField.setDisable(isString);
        keyInfoBatchDeleteButton.setVisible(!isString);
        redisKeyInfoTableView.setVisible(!isString);
        redisKeyInfoTableView.setManaged(!isString);
        keyInfoValueSizeLabel.setVisible(isString);
        keyInfoValueSizeLabel.setManaged(isString);
        keyInfoValueSizeText.setVisible(isString);
        keyInfoValueSizeText.setManaged(isString);
        pageRowComboBox.setVisible(!isString);
        resultFirstLabel.setVisible(!isString);
        resultPreviousLabel.setVisible(!isString);
        resultNextLabel.setVisible(!isString);
        resultLastLabel.setVisible(!isString);
        keyInfoGotoPageTextField.setVisible(!isString);
        keyInfoTotalPageLabel.setVisible(!isString);

        keyInfoBorderPane.setCenter(null);
        keyInfoBorderPane.setLeft(null);
        if(isString){
            keyInfoValueTextArea.setEditable(true);
            keyInfoBorderPane.setCenter(keyInfoValueTextArea);
            keyInfoValueSizeLabel.setText(StringUtils.getLengthHumman(keyInfo.getValue().length));
        }else{
            keyInfoBorderPane.setCenter(redisKeyInfoTableView);
            //更新表格
            if(DataStructureUtils.isNotEmpty(keyInfo.getRedisKeyDataList())){
                redisKeyInfoTableView.setItems(FXCollections.observableArrayList(keyInfo.getRedisKeyDataList()));
            }else{
                redisKeyInfoTableView.getItems().clear();
            }
            redisKeyInfoTableView.refresh();
            changeTableShowType(keyInfoTableShowTypeCombox.getValue());
        }
        keyInfoTotalPageLabel.setText("共"+pageBean.getTotalPage()+"页");
        if(RedisTreeType.LIST.equals(keyInfo.getType())){
            keyInfoFilterTextField.setPromptText("输入筛选条件,按ENTER键筛选当页");
            keyInfoGotoPageTextField.setDisable(false);
            resultFirstLabel.setDisable(pageBean.isFirstPage());
            resultPreviousLabel.setDisable(!pageBean.hasPreviousPage());
            resultNextLabel.setDisable(!pageBean.hasNextPage());
            resultLastLabel.setDisable(pageBean.isLastPage());
            keyInfoGotoPageTextField.setText(pageBean.getPage()+"");
        }else{
            keyInfoFilterTextField.setPromptText("输入筛选条件,按ENTER键搜索");
            keyInfoGotoPageTextField.setDisable(true);
            resultFirstLabel.setDisable(true);
            resultPreviousLabel.setDisable(true);
            resultNextLabel.setDisable(true);
            resultLastLabel.setDisable(true);
            keyInfoGotoPageTextField.setText("1");
        }


    }

    @Override
    public void close() throws IOException {
        this.selectedItem = null;
        this.keyInfo = null;
        this.tab = null;
    }
}
